#ifndef __SC_DATA_H__
#define __SC_DATA_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <string>
#include <map>

#include "SCSystem.hpp"
#include "SCFuncPtrWrapper.hpp"

#include "Range.hpp"
#include "DynamicLibrary.hpp"

#include "SC_LibConfig.h"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Forward declarations
////////////////////////////////////////////////////////////////////////////////

class SCProgram;
class SComputationInfo;
class SCSystemChanges;

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

/**
 *  This class defines the "Systemic Computation" data for a simulation.
 */
class LIBRARY_DECL SCData {

	public:
		
		/// Constructor
		SCData();
		
		/// Destroys the data
		~SCData();
		
		/// Initialise from the given program with the given plugins directories
		void Init(const SCProgram &, const std::vector<std::string> & = std::vector<std::string>());
		
		// Returns the number of systems
		const unsigned int GetNbSystems() const { return this->systems.size(); }
		
		/// Returns the ith system (read-only)
		const SCSystem & GetSystem(const unsigned int) const;
		
		/// Returns the ith system
		SCSystem & System(const unsigned int);
		
		/// Returns the systems in read-only mode
		const std::vector<SCSystem> & GetSystems() const { return this->systems; }
		
		/// Returns the universe (root of the system tree) (read-only)
		const SCSystem & GetUniverse() const;
		
		/// Returns the universe (root of the system tree)
		SCSystem & Universe();
		
		/// Returns the context list (read-only)
		std::vector<SCSystem *> & Contexts() { return this->contexts; }
		
		/// Returns the context list
		const std::vector<SCSystem *> & GetContexts() const { return this->contexts; }
		
		/// Returns the ith context
		SCSystem & Context(const unsigned int);
		
		/// Add the given context to the context list
		const bool AddContext(SCSystem &);
		
		/// Remove the given context from the context list
		void RemoveContext(SCSystem &);
		
		/// Returns the word length
		const unsigned int GetWordLength() const { return this->word_length; }
		
		/// Returns the code map
		const std::map<char,std::string> & GetCodeMap() const { return this->code_map; }

		/// Returns the function map
		const std::map<std::string,SCFuncPtrWrapper> & GetFuncMap() const { return this->func_map; }

		/// Returns the function code kernel offset bounds
		const Types::Range<int> & GetFuncOffset() const { return this->func_offset; }
		
		/// Clear the data
		void Clear();
		
		/// Apply computation to the current data
		void ApplyComputation(const SComputationInfo &);

	protected:

		/// Word length
		unsigned int word_length;

		/// System list
		std::vector<SCSystem> systems;

		/// Sublist of the previous one including only context systems pointers
		std::vector<SCSystem*> contexts;

		/// Compression code map
		std::map<char,std::string> code_map;
		
		/// Loaded plugins list
		std::vector<Toolkit::DynamicLibrary> plugins;

		/// Function code to pointer map
		std::map<std::string,SCFuncPtrWrapper> func_map;

		/// Function code kernel offset bounds (<0 if none)
		Types::Range<int> func_offset;
		
		/// Apply the given changes to the given systems
		void apply_changes(SCSystem &, const SCSystemChanges &);

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif
